package database

import (
	"context"
	"fmt"
	"log"
	"time"

	"iot-cloud-platform/internal/config"

	"github.com/redis/go-redis/v9"
)

// InitRedis initializes Redis connection with clustering support
func InitRedis(cfg config.RedisConfig) (*redis.Client, error) {
	addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)

	client := redis.NewClient(&redis.Options{
		Addr:         addr,
		Password:     cfg.Password,
		DB:           cfg.DB,
		PoolSize:     100,
		MinIdleConns: 10,
	})

	// Test connection
	ctx := context.Background()
	_, err := client.Ping(ctx).Result()
	if err != nil {
		return nil, fmt.Errorf("failed to connect to Redis: %w", err)
	}

	log.Println("Redis connected successfully")
	return client, nil
}

// RedisManager provides high-level Redis operations for the IoT platform
type RedisManager struct {
	client *redis.Client
}

// NewRedisManager creates a new Redis manager
func NewRedisManager(client *redis.Client) *RedisManager {
	return &RedisManager{
		client: client,
	}
}

// SetDeviceStatus sets device online/offline status with expiration
func (r *RedisManager) SetDeviceStatus(ctx context.Context, deviceID string, status string, expiration int) error {
	key := fmt.Sprintf("device:status:%s", deviceID)
	return r.client.Set(ctx, key, status, time.Duration(expiration)*time.Second).Err()
}

// GetDeviceStatus gets device status from cache
func (r *RedisManager) GetDeviceStatus(ctx context.Context, deviceID string) (string, error) {
	key := fmt.Sprintf("device:status:%s", deviceID)
	return r.client.Get(ctx, key).Result()
}

// SetDeviceLastSeen updates device last seen timestamp
func (r *RedisManager) SetDeviceLastSeen(ctx context.Context, deviceID string, timestamp int64) error {
	key := fmt.Sprintf("device:lastseen:%s", deviceID)
	return r.client.Set(ctx, key, timestamp, 0).Err()
}

// GetDeviceLastSeen gets device last seen timestamp
func (r *RedisManager) GetDeviceLastSeen(ctx context.Context, deviceID string) (int64, error) {
	key := fmt.Sprintf("device:lastseen:%s", deviceID)
	return r.client.Get(ctx, key).Int64()
}

// CacheDeviceShadow caches device shadow data
func (r *RedisManager) CacheDeviceShadow(ctx context.Context, deviceID string, shadow string) error {
	key := fmt.Sprintf("device:shadow:%s", deviceID)
	return r.client.Set(ctx, key, shadow, time.Hour).Err() // 1 hour cache
}

// GetCachedDeviceShadow gets cached device shadow
func (r *RedisManager) GetCachedDeviceShadow(ctx context.Context, deviceID string) (string, error) {
	key := fmt.Sprintf("device:shadow:%s", deviceID)
	return r.client.Get(ctx, key).Result()
}

// SetUserSession stores user session data
func (r *RedisManager) SetUserSession(ctx context.Context, sessionID string, userData string, expiration int) error {
	key := fmt.Sprintf("session:%s", sessionID)
	return r.client.Set(ctx, key, userData, time.Duration(expiration)*time.Second).Err()
}

// GetUserSession gets user session data
func (r *RedisManager) GetUserSession(ctx context.Context, sessionID string) (string, error) {
	key := fmt.Sprintf("session:%s", sessionID)
	return r.client.Get(ctx, key).Result()
}

// DeleteUserSession deletes user session
func (r *RedisManager) DeleteUserSession(ctx context.Context, sessionID string) error {
	key := fmt.Sprintf("session:%s", sessionID)
	return r.client.Del(ctx, key).Err()
}

// IncrementRateLimit increments rate limit counter for a key
func (r *RedisManager) IncrementRateLimit(ctx context.Context, key string, window int) (int64, error) {
	pipe := r.client.Pipeline()

	incr := pipe.Incr(ctx, key)
	pipe.Expire(ctx, key, time.Duration(window)*time.Second)

	_, err := pipe.Exec(ctx)
	if err != nil {
		return 0, err
	}

	return incr.Val(), nil
}

// SetTemporaryData sets temporary data with expiration
func (r *RedisManager) SetTemporaryData(ctx context.Context, key string, value string, expiration int) error {
	return r.client.Set(ctx, key, value, time.Duration(expiration)*time.Second).Err()
}

// GetTemporaryData gets temporary data
func (r *RedisManager) GetTemporaryData(ctx context.Context, key string) (string, error) {
	return r.client.Get(ctx, key).Result()
}

// DeleteTemporaryData deletes temporary data
func (r *RedisManager) DeleteTemporaryData(ctx context.Context, key string) error {
	return r.client.Del(ctx, key).Err()
}

// PublishMessage publishes a message to a Redis channel
func (r *RedisManager) PublishMessage(ctx context.Context, channel string, message string) error {
	return r.client.Publish(ctx, channel, message).Err()
}

// SubscribeToChannel subscribes to a Redis channel
func (r *RedisManager) SubscribeToChannel(ctx context.Context, channels ...string) *redis.PubSub {
	return r.client.Subscribe(ctx, channels...)
}

// GetClient returns the underlying Redis client
func (r *RedisManager) GetClient() *redis.Client {
	return r.client
}
